<html>
    <head>
        <title>用于配置扩展属性的选项</title>
    </head>
    <body>
        <script>
            /*
                        二、用于配置属性的选项  Property Options

                                你可以在定义新属性时配置一些选项。

                                1、DisplayName 显示名称

                                        你可能希望为用户交互界面上显示的属性设置不同的（人类可读的）显示名称。

                                        ①、如果你不想本地化？

                                                如果你的应用程序没有本地化，你可以直接将属性的DisplayName设置为FixedLocalizableString对象。示例：

                                                        property =>
                                                        {
                                                            property.DisplayName = new FixedLocalizableString("Social security no");
                                                        }

                                        ②、本地化显示名称

                                                如果要本地化显示名称，有两个选项：

                                                    (1)、按照惯例本地化
                                                        
                                                            你可以直接打开本地化文件（如en.json）并将以下条目添加到texts部分，而不是设置property.DisplayName：

                                                                "SocialSecurityNumber": "Social security no"

                                                                在本地化文件中为你支持的每种语言定义相同的SocialSecurityNumber键（你之前定义的属性名称）。就是这么简单！

                                                            在某些情况下，本地化键可能与本地化文件中的其他键冲突。

                                                            在这种情况下，你可以使用DisplayName:前缀作为本地化文件中显示名称的前缀（DisplayName:SocialSecurityNumber作为本例的本地化键）。

                                                            扩展系统首先查找前缀版本，然后回退到无前缀名称（如果你没有本地化它，它会回退到属性名称）。

                                                            推荐这种方法，因为它简单且适用于大多数场景。

                                                    (2)、使用DispalyName属性进行本地化

                                                            如果要指定本地化密钥或本地化资源，仍然可以设置DisplayName选项：

                                                                        property =>
                                                                        {
                                                                            property.DisplayName =
                                                                                LocalizableString.Create<MyProjectNameResource>(
                                                                                    "UserSocialSecurityNumberDisplayName"
                                                                                );
                                                                        }

                                                                        MyProjectNameResource是本地化资源，UserSocialSecurityNumberDisplayName是本地化资源中的本地化键名。

                                2、默认值

                                        自动为新属性设置默认值，这是属性类型的自然默认值，例如string的null，false或int的0。

                                        有两种方法可以覆盖默认值：

                                            ①、DefaultValue选项

                                                    DefaultValue选项可以设置为任何值：

                                                            property =>
                                                            {
                                                                property.DefaultValue = 42;
                                                            }
                                                                
                                            ②、 DefaultValueFactory选项
                                                                                    
                                                     DefaultValueFactory可以设置为返回默认值的函数：
                                                     
                                                        property =>
                                                        {
                                                            property.DefaultValueFactory = () => DateTime.Now;
                                                        }

                                            options.DefaultValueFactory的优先级高于options.DefaultValue。

                                        
                                           提示：仅当默认值可能随时间变化时才使用DefaultValueFactory选项（如本例中的DateTime.Now）。如果它是一个常量值，则使用DefaultValue选项。


                                3、Validation 验证

                                        实体扩展系统允许你以多种方式定义扩展属性的验证。

                                        ①、数据注释特定

                                                            Attributes是与此属性关联的特性列表。

                                                            下面的示例代码将两个数据注释验证特性添加到属性中：

                                                                property =>
                                                                {
                                                                    property.Attributes.Add(new RequiredAttribute());
                                                                    property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4});
                                                                }

                                                        当你运行应用程序时，你会看到验证开箱即用：  表单上会有相关验证。

                                                        由于我们已经添加了RequiredAttribute，它不允许将其留空。验证系统工作：

                                                                在用户交互界面上（带有自动本地化）。

                                                                在HTTP API上。即使你直接执行HTTP请求，你也会收到带有正确HTTP状态代码的验证错误

                                                                在实体的SetProperty(...)方法上（如果你想知道SetProperty()方法是什么，请参阅文档）。https://abp.io/docs/latest/framework/architecture/domain-driven-design/entities

                                                        因此，它会自动进行全栈验证。
                                        
                                        ②、默认验证特性：

                                                  当你创建某些类型的属性时，会自动添加一些特性：

                                                        RequiredAttribute。如果要允许空值，请使属性为空（例如int?）。

                                                        EnumDataTypeAttribute，以防止设置无效的枚举值。

                                                  使用property.Attributes.Clear();如果你不想要这些特性。

                                         
                                        ③、验证操作

                                                  验证操作允许你执行自定义代码来执行验证。

                                                  下面的示例检查SocialSecurityNumber是否以B开头，如果是，则添加验证错误：

                                                        property =>
                                                        {
                                                            property.Attributes.Add(new RequiredAttribute());
                                                            property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4});

                                                            property.Validators.Add(context =>
                                                            {
                                                                if (((string) context.Value).StartsWith("B"))
                                                                {
                                                                    context.ValidationErrors.Add(
                                                                        new ValidationResult(
                                                                            "Social security number can not start with the letter 'B', sorry!",
                                                                            new[] {"extraProperties.SocialSecurityNumber"}
                                                                        )
                                                                    );
                                                                }
                                                            });

                                                        }
                                                  在这种情况下使用RegularExpressionAttribute可能更好，但这只是一个例子。

                                                  无论如何，如果你输入一个以字母B开头的值，你在保存表单时会收到以下错误：

                                                  ★、上下文对象：

                                                        context对象具有可用于自定义验证操作的有用属性。

                                                        例如，你可以使用context.ServiceProvider来解析来自依赖注入系统的服务。

                                                        下面的示例获取本地化程序并添加本地化错误消息：

                                                                if (((string) context.Value).StartsWith("B"))
                                                                {
                                                                    var localizer = context.ServiceProvider
                                                                        .GetRequiredService<IStringLocalizer<MyProjectNameResource>>();

                                                                    context.ValidationErrors.Add(
                                                                        new ValidationResult(
                                                                            localizer["SocialSecurityNumberCanNotStartWithB"],
                                                                            new[] {"extraProperties.SocialSecurityNumber"}
                                                                        )
                                                                    );
                                                                }

                                                    注意： context.ServiceProvider可以为空！只有当你在对象上使用SetProperty(...)方法时，它才能为null。

                                                           它才能为null。因为DI系统此时不可用。

                                                           虽然这是一种罕见的情况，但当context.ServiceProvider为null时，你应该执行兜底逻辑。

                                                           对于此示例，你将添加非本地化错误消息。

                                                           这不是问题，因为将无效值设置为属性通常是程序员的错误，在这种情况下你大多不需要本地化。

                                                           论如何，即使在常规属性设置器中，你也无法使用本地化。

                                                           但是，如果你对本地化很认真，你可以抛出业务异常（请参阅异常处理文档以了解如何本地化业务异常）。
                                 
                                4、UI 可见性

                                            当你定义一个属性时，它会出现在数据表上，在相关的UI页面上创建和编辑表单。但是，你可以单独控制每个。
                                            
                                            示例：
                                                    property =>
                                                    {
                                                        property.UI.OnTable.IsVisible = false;
                                                        //...other configurations
                                                    }

                                            使用property.UI.OnCreateForm和property.UI.OnEditForm来控制表单。

                                            。如果需要属性，但未添加到创建表单中，你肯定会得到验证异常，因此请小心使用此选项。
                                            
                                            但是如果你需要，编辑表单中可能没有必需的属性。

                                5、UI中显示的顺序

                                            当你定义一个属性时，它会出现在数据表上，在相关的UI页面上创建和编辑表单。但是，你可以控制它的顺序。
                                            
                                            示例：
                                                    property =>
                                                    {
                                                        property.UI.Order = 1;
                                                        //...other configurations
                                                    }
                                            使用property.UI.OnCreateForm和property.UI.OnEditForm来控制表单。

                                            如果需要属性，但未添加到创建表单中，你肯定会得到验证异常，因此请小心使用此选项。

                                            但是如果你需要，编辑表单中可能没有必需的属性。

                                6、HTTP API可用性

                                            即使你禁用了UI上的属性，它仍然可以通过HTTP API获得。默认情况下，所有API上都可以使用属性。

                                            使用property.Api选项使属性在某些API端点中不可用。

                                                property =>
                                                {
                                                    property.Api.OnUpdate.IsAvailable = false;
                                                }     
                                                    
                                            在此示例中，更新HTTP API将不允许为此属性设置新值。

                                            在这种情况下，你还希望在编辑表单上禁用此属性：

                                                property =>
                                                {
                                                    property.Api.OnUpdate.IsAvailable = false;
                                                    property.UI.OnEditForm.IsVisible = false;
                                                }

                                            除了property.Api.OnUpdate之外，你还可以设置property.Api.OnCreate和property.Api.OnGet以精细控制API端点。
                    */

        </script>
    </body>
</html>